package config

import (
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"log"
	"sync"
	"time"

	_ "github.com/go-sql-driver/mysql"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

var c *Config

func C() *Config {
	if c == nil {
		log.Panic("请先初始化配置")
	}
	return c
}

type Config struct {
	Mysql *Mysql `json:"mysql"`
	Http  *Http  `json:"http"`
	Grpc  *Grpc  `json:"grpc"`
}
type Mysql struct {
	Host     string `json:"host" env:"MYSQL_HOST"`
	Port     int    `json:"port" env:"MYSQL_PORT"`
	Database string `json:"database" env:"MYSQL_DATABASE"`
	Username string `json:"username" env:"MYSQL_USERNAME"`
	Password string `json:"password" env:"MYSQL_PASSWORD"`

	// 高级参数
	MaxOpenConn int `env:"MYSQL_MAX_OPEN_CONN"`
	MaxIdleConn int `env:"MYSQL_MAX_IDLE_CONN"`
	MaxLifeTime int `env:"MYSQL_MAX_LIFE_TIME"`
	MaxIdleTime int `env:"MYSQL_MAX_IDLE_TIME"`

	lock sync.Mutex
	db   *gorm.DB
}

type Http struct {
	Host string `json:"host" env:"HTTP_HOST"`
	Port int    `json:"port" env:"HTTP_PORT"`
}

type Grpc struct {
	Host string `json:"host" env:"GRPC_HOST"`
	Port int    `json:"port" env:"GRPC_PORT"`
}

func DefaultConfig() *Config {
	return &Config{
		Mysql: newDefaultMySQL(),
		Http:  newDefaultHttp(),
		Grpc:  newDefaultGrpc(),
	}
}

func newDefaultMySQL() *Mysql {
	return &Mysql{
		Host:     "127.0.0.1",
		Port:     3306,
		Database: "vblog",
		Username: "root",
		Password: "root",
	}
}

func newDefaultHttp() *Http {
	return &Http{
		Host: "127.0.0.1",
		Port: 8080,
	}
}

func newDefaultGrpc() *Grpc {
	return &Grpc{
		Host: "127.0.0.1",
		Port: 9090,
	}
}

func (c *Config) ToJson() string {
	if data, err := json.Marshal(c); err == nil {
		return string(data)
	} else {
		log.Println(err.Error())
	}
	return "[]"
}

func (m *Mysql) GetConnPool() (*sql.DB, error) {

	//username:password@protocol(address)/dbname?charset=utf8mb4&parseTime=True&loc=Local
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", m.Username, m.Password, m.Host, m.Port, m.Database)
	db, err := sql.Open("mysql", dsn)

	if err != nil {
		panic(err)
	}
	if m.MaxOpenConn > 0 {
		db.SetMaxOpenConns(m.MaxOpenConn)
	}
	if m.MaxIdleConn > 0 {
		db.SetMaxIdleConns(m.MaxIdleConn)
	}
	if m.MaxLifeTime != 0 {
		db.SetConnMaxLifetime(time.Second * time.Duration(m.MaxLifeTime))
	}
	if m.MaxIdleTime != 0 {
		db.SetConnMaxIdleTime(time.Second * time.Duration(m.MaxIdleTime))
	}

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	if err := db.PingContext(ctx); err != nil {
		return nil, fmt.Errorf("ping mysql<%s> error, %s", dsn, err.Error())
	}
	return db, nil
}

func (m *Mysql) ORM() *gorm.DB {
	// 这里并发没控制不会生效应该放到判断条件外部
	m.lock.Lock()
	defer m.lock.Unlock()

	if m.db == nil {
		conn, err := m.GetConnPool()
		if err != nil {
			log.Panic(err)
		}
		gormDB, err := gorm.Open(mysql.New(mysql.Config{
			Conn: conn,
		}), &gorm.Config{
			// GORM perform single create, update, delete operations in transactions by default to ensure database data integrity
			// You can disable it by setting `SkipDefaultTransaction` to true
			SkipDefaultTransaction: true,
			// PrepareStmt executes the given query in cached statement
			PrepareStmt: true,
			Logger:      logger.Default.LogMode(logger.Info),
		})
		if err != nil {
			log.Panic(err)
		}
		m.db = gormDB
	}
	return m.db
}

func (h *Http) Address() string {
	return fmt.Sprintf("%s:%d", h.Host, h.Port)
}
